﻿//
// Created by qsy on 2023/8/17.
//
//==================================================================================================
//| 文件名称 | Modbus.c
//|--------- |--------------------------------------------------------------------------------------
//| 文件描述 | Modbus相关文件
//|--------- |--------------------------------------------------------------------------------------
//| 运行环境 | 所有C/++语言编译器，包括单片机编译器
//|--------- |--------------------------------------------------------------------------------------
//| 版权声明 | Copyright 2017 Zhejiang University ME Dep.
//|----------|--------------------------------------------------------------------------------------
//|  版本    |  时间       |  作者     | 描述
//|----------|-------------|-----------|------------------------------------------------------------
//|  V1.0    | 2017.05.23  | FU LEI    |
//==================================================================================================
#include <string.h>
#include <stdio.h>
#include "main.h"
#include "Modbus_Slave.h"
#include "usart.h"
#include "Read_Pin.h"
#include "trial.h"
#include "task.h"
#include "tim.h"

uint16_t g_HoldRegData[HOLDREG_LENGTH] = {  0, 0, 0, 0, 0, 0};
uint8_t Usart3TxBuf[HOLDREG_LENGTH];   // USART3 发送缓存结构体变量


MODBUS_MASTER_CMD ModbusMasterCMD;


//==================================================================================================
//| 函数名称 | Modbus_Frm_Check()
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 对接收到的数据帧进行识别 确定是否为有效帧
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================
void Modbus_Frm_Check(void)
{
    uint8_t index,i , tempuint8_t[8] , tempuint8_tH,tempuint8_tL;
    uint16_t  tempuint16_t;

    if( USARTx_RX_BUF_MSG.NewFrmFlag == 1 )
    {
        for(index = 0;index< USARTx_RX_BUF_MSG.RXIndex ; index++)
        {
            // 确保找到Device ID 同时保证 ID后面还有8个字节
            if( USARTx_RX_BUF_MSG.Buf[index]==MODBUS_DEVICE_ID && USARTx_RX_BUF_MSG.RXIndex - index >=7)
            {
                for(i=0;i<8;i++)
                    tempuint8_t[i] = USARTx_RX_BUF_MSG.Buf[index++];   // 连续copy 8个字节到tempuint8_t

                tempuint16_t =  CRC_Check(tempuint8_t,6);
                tempuint8_tH = (unsigned char)(tempuint16_t >> 8 );
                tempuint8_tL = (unsigned char)(tempuint16_t & 0x00FF);

                if( tempuint8_t[6] == tempuint8_tH && tempuint8_t[7] == tempuint8_tL) //	tempuint8_t[6]为收到的CRC低字节，tempuint16_t低字节在高地址了，tempuint8_tH取到的就是重新校验得到的CRC低字节
                {
                    ModbusMasterCMD.IDAddr    = tempuint8_t[0];
                    ModbusMasterCMD.FunCMD    = tempuint8_t[1];
                    ModbusMasterCMD.RegAddrH  = tempuint8_t[2];
                    ModbusMasterCMD.RegAddrL  = tempuint8_t[3];
                    ModbusMasterCMD.DataH     = tempuint8_t[4];
                    ModbusMasterCMD.DataL     = tempuint8_t[5];
                    ModbusMasterCMD.CRCCheckL = tempuint8_t[6];
                    ModbusMasterCMD.CRCCheckH = tempuint8_t[7];

                    ModbusMasterCMD.NewFrmFlag = 1;     // 置起标志位，开始MODBUS FRAME 内容解析响应

                    break; // 表示已经找到正确一帧信息，结束在缓冲区中查找 立即跳出结束
                }

            }

        }
        USARTx_RX_BUF_MSG.NewFrmFlag = 0;
        USARTx_RX_BUF_MSG.RXIndex = 0;
    }
}

//==================================================================================================
//| 函数名称 | Modbus_Frm_Respond()
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 对不同的MODBUS帧进行响应
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================
void Modbus_Frm_Respond(void)
{
    if(ModbusMasterCMD.NewFrmFlag == 1)
    {
        switch(ModbusMasterCMD.FunCMD)
        {
            // 03H功能码，读取保持寄存器
            case READ_HOLDING_REG_03H:
            {
                //printf("Read holding register\r\n");
                Modbus_03HCMD_Handle();
                break;
            }

                // 04H功能码，读取输入寄存器
            case READ_INPUT_REG_04H:
            {
                Modbus_04HCMD_Handle();
                break;
            }

                // 05H 功能码 写入单个线圈
            case WRITE_SINGLE_COIL_05H:
            {
                //printf("Write a single coil\r\n");
                Modbus_05HCMD_Handle();
                break;
            }

                // 06H 功能码 写入单个字
            case WRITE_SINGLE_REG_06H:
            {
                Modbus_06HCMD_Handle();
                break;
            }

            default:
                break;

        }
        ModbusMasterCMD.NewFrmFlag  = 0;
    }
}



//==================================================================================================
//| 函数名称 | Modbus_03HCMD_Handle()
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 针对03H功能码的数据进行相关的处理，可以回复不同字节数的函数-
//|          | 优化过了，比以前只能响应单个字节效率高很多
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================
void Modbus_03HCMD_Handle(void)
{
    uint16_t   index =0, loop;
    uint16_t  Crc_res;

    Usart3TxBuf[index++] = MODBUS_DEVICE_ID;                          // 主站号
    Usart3TxBuf[index++] = READ_HOLDING_REG_03H;                      // 功能码
    Usart3TxBuf[index++] = ModbusMasterCMD.DataL * sizeof(uint16_t) ;   // 回复的字节数

    for(loop=0; loop< ModbusMasterCMD.DataL ; loop++)
    {
        Usart3TxBuf[index++] =  uint16_t_GetHighByte( g_HoldRegData[ModbusMasterCMD.RegAddrL + loop]) ;
        Usart3TxBuf[index++] =  uint16_t_GetLowByte(  g_HoldRegData[ModbusMasterCMD.RegAddrL + loop]) ;
    }

    Crc_res = CRC_Check(Usart3TxBuf, index) ;

    Usart3TxBuf[index++] = uint16_t_GetHighByte( Crc_res);             // CRC 校验
    Usart3TxBuf[index++] = uint16_t_GetLowByte(Crc_res);	//	最后一个字节了，不用加

    USARTx_DataTX(Usart3TxBuf, index);  // 启动发送
}
//==================================================================================================
//| 函数名称 | Modbus_04HCMD_Handle
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 04H 功能码的响应函数，将单个线圈值写入
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================
void Modbus_04HCMD_Handle(void)
{




}
//==================================================================================================
//| 函数名称 | Modbus_05HCMD_Handle
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 05H 功能码的响应函数，将单个线圈值写入
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================

void Modbus_05HCMD_Handle(void)
{
    uint8_t	index = 0;

    //主机发送数据包括：从站地址+功能码+寄存器起始地址+数据值+校验码

    switch(ModbusMasterCMD.RegAddrL)
    {
        // -- 测试modbus调试用----
        case  0:
        {
            break;
        }

        case  1:
        {
            //开始测试
            if (ModbusMasterCMD.DataH == 0xFF)
            {
                SystemStatus.Trial_Stop = 0;    //清除停止标志
                SystemStatus.Trial_Idle = 0;    //跑出空闲
                SystemStatus.Trial_Ready = 1;   //进入准备
                SystemStatus.Trial_On = 1;      //进入运行
                TrialData.TimSet = (uint32_t)(ModbusMasterCMD.DataL * 1000);
                break;
            }
            //停止测试
            else
            {
                PrecisionParameters = (uint8_t)ModbusMasterCMD.DataL;
                SystemStatus.Trial_Ready = 0;   //清零准备标志位
                SystemStatus.Trial_On = 0;      //清零运行标志位
                SystemStatus.Trial_Stop = 1;    //停止运行，进入状态3
                SystemStatus.Trial_Idle = 1;    //停止过后进入空闲状态

                //PrecisionParameters 最小为2 不能设置成1
                if (PrecisionParameters - 1)
                {
                    MX_TIM4_Init();
                }

                break;
            }

        }

        case  2:
        {
            break;
        }
        //数据清零
        case  3:
        {
            //清零操作
            for (int i = 0; i < 16; ++i) {
                lw_PulseCntNum[i] = 0;
            }
            break;
        }

        default:

            break;
    }

    //----------原文回复MODBUS 命令--------
    Usart3TxBuf[index++] = ModbusMasterCMD.IDAddr;
    Usart3TxBuf[index++] = ModbusMasterCMD.FunCMD;
    Usart3TxBuf[index++] = ModbusMasterCMD.RegAddrH;
    Usart3TxBuf[index++] = ModbusMasterCMD.RegAddrL;
    Usart3TxBuf[index++] = ModbusMasterCMD.DataH;
    Usart3TxBuf[index++] = ModbusMasterCMD.DataL;
    Usart3TxBuf[index++] = ModbusMasterCMD.CRCCheckL;
    Usart3TxBuf[index++] = ModbusMasterCMD.CRCCheckH;

    USARTx_DataTX(Usart3TxBuf, index);  // 启动发送

    //----------End of 原文回复MODBUS 命令--------

}


//==================================================================================================
//| 函数名称 | Modbus_05HCMD_Handle
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 06H 功能码的响应函数，将单个寄存器值写入
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================
void Modbus_06HCMD_Handle(void)
{

}



//==================================================================================================
//| 函数名称 | U32_GetHighByte   U32_GetLowByte   U32_TranWord
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 32位 和 16位数据的相互转换
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================
uint16_t U32_GetHighWord(uint32_t data)
{
    uint16_t result;
    result = (uint16_t)( (data >> 16) & 0x0000FFFF);

    return result;
}

uint16_t U32_GetLowWord(uint32_t data)
{
    uint16_t result;
    result = (uint16_t)( data & 0x0000FFFF);

    return result;

}

uint32_t U32_TranWord(uint16_t dataH, uint16_t dataL)
{
    uint32_t result;
    result =  ( ( (uint32_t)dataH ) << 16 ) & 0xFFFF0000;
    result = result +  dataL;

    return result;
}

//==================================================================================================
//| 函数名称 | uint16_t_GetHighByte   uint16_t_GetLowByte   uint16_t_TranWord
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 16位 和 8位数据的相互转换
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================
uint8_t uint16_t_GetHighByte(uint16_t data)
{
    uint8_t result;
    result = (uint8_t)( (data >> 8) & 0x00FF);

    return result;
}

uint8_t uint16_t_GetLowByte(uint16_t data)
{
    uint8_t result;
    result = (uint8_t)( data & 0x00FF);

    return result;

}

uint16_t uint16_t_TranWord(uint8_t dataH, uint8_t dataL)
{
    uint16_t result;
    result =  ( ( (uint16_t)dataH ) << 8 ) & 0xFF00;
    result = result +  dataL;

    return result;
}

//==================================================================================================
//| 函数名称 | CRC_Check()
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 计算CRC校验和，用于MODBUS通讯中的数据校验
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | unsigned char* data, unsigned char length
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | unsigned int :reg_crc
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 | CRC校验和计算，需要校验数据的首地址和数据长度，已经低字节在前了~~，
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2015.02.23
//==================================================================================================
uint16_t CRC_Check(uint8_t* data, uint8_t length)
{
    int j,crc_low,crc_high;
    unsigned int reg_crc=0xFFFF;
    while(length--){
        reg_crc ^= *data++;
        for(j=0;j<8;j++)
        {
            if(reg_crc & 0x01)
            { /* LSB(b0)=1 */
                reg_crc=(reg_crc>>1) ^ 0xA001;
            }
            else
            {
                reg_crc=reg_crc >>1;
            }
        }
    }
    //	经过以上处理CRC为正常的高字节在前，低字节在后，为了适配有些协议要求，为先发送低字节，再发送高字节
    //	所以这里直接转换下，把低字节放在高字节位置，则发送高字节的话实质上就是把低字节发送出去了
    crc_low = reg_crc  & 0x00FF;
    crc_high = (reg_crc >> 8) & 0x00FF ;

    reg_crc = crc_low * 256 + crc_high;

    return reg_crc; // 最后回传 CRC 缓存器的值
}
//==================================================================================================
//| 函数名称 | HoldRegDataUpdate()
//|----------|--------------------------------------------------------------------------------------
//| 函数功能 | 03H寄存器 过程映像刷新
//|----------|--------------------------------------------------------------------------------------
//| 输入参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 返回参数 | void
//|----------|--------------------------------------------------------------------------------------
//| 描述说明 |
//|----------|--------------------------------------------------------------------------------------
//| 函数设计 | FU,2017.05.23
//==================================================================================================

void HoldRegDataUpdate(void)
{
//    for(int i = 0;i < 16;i++)
//    {
//        g_HoldRegData[i*4] = uint16_t_GetHighByte(U32_GetHighByte(data_buf_send[i]));
//        g_HoldRegData[i*4+1] = uint16_t_GetLowByte(U32_GetHighByte(data_buf_send[i]));
//        g_HoldRegData[i*4+2] = uint16_t_GetHighByte(U32_GetLowByte(data_buf_send[i]));
//        g_HoldRegData[i*4+3] = uint16_t_GetLowByte(U32_GetLowByte(data_buf_send[i]));
//    }

    g_HoldRegData[0] = 1234;
    g_HoldRegData[1] = 0;

    g_HoldRegData[2] = U32_GetHighWord(lw_PulseCntNum[0]);   // 脉冲通道0计数的高16位
    g_HoldRegData[3] = U32_GetLowWord( lw_PulseCntNum[0]);   // 脉冲通道0计数的低16位
    g_HoldRegData[4] = U32_GetHighWord(lw_PulseCntNum[1]);   // 脉冲通道1计数的高16位
    g_HoldRegData[5] = U32_GetLowWord( lw_PulseCntNum[1]);   // 脉冲通道1计数的低16位
    g_HoldRegData[6] = U32_GetHighWord(lw_PulseCntNum[2]);   // 脉冲通道2计数的高16位
    g_HoldRegData[7] = U32_GetLowWord( lw_PulseCntNum[2]);   // 脉冲通道2计数的低16位
    g_HoldRegData[8] = U32_GetHighWord(lw_PulseCntNum[3]);   // 脉冲通道3计数的高16位
    g_HoldRegData[9] = U32_GetLowWord( lw_PulseCntNum[3]);   // 脉冲通道3计数的低16位

    g_HoldRegData[10] = U32_GetHighWord(lw_PulseCntNum[4]);   // 脉冲通道4计数的高16位
    g_HoldRegData[11] = U32_GetLowWord( lw_PulseCntNum[4]);   // 脉冲通道4计数的低16位
    g_HoldRegData[12] = U32_GetHighWord(lw_PulseCntNum[5]);   // 脉冲通道5计数的高16位
    g_HoldRegData[13] = U32_GetLowWord( lw_PulseCntNum[5]);   // 脉冲通道5计数的低16位
    g_HoldRegData[14] = U32_GetHighWord(lw_PulseCntNum[6]);   // 脉冲通道6计数的高16位
    g_HoldRegData[15] = U32_GetLowWord( lw_PulseCntNum[6]);   // 脉冲通道6计数的低16位
    g_HoldRegData[16] = U32_GetHighWord(lw_PulseCntNum[7]);   // 脉冲通道7计数的高16位
    g_HoldRegData[17] = U32_GetLowWord( lw_PulseCntNum[7]);   // 脉冲通道7计数的低16位

    g_HoldRegData[18] = U32_GetHighWord(lw_PulseCntNum[8]);   // 脉冲通道8计数的高16位
    g_HoldRegData[19] = U32_GetLowWord( lw_PulseCntNum[8]);   // 脉冲通道8计数的低16位
    g_HoldRegData[20] = U32_GetHighWord(lw_PulseCntNum[9]);   // 脉冲通道9计数的高16位
    g_HoldRegData[21] = U32_GetLowWord( lw_PulseCntNum[9]);   // 脉冲通道9计数的低16位
    g_HoldRegData[22] = U32_GetHighWord(lw_PulseCntNum[10]);   // 脉冲通道10计数的高16位
    g_HoldRegData[23] = U32_GetLowWord( lw_PulseCntNum[10]);   // 脉冲通道10计数的低16位
    g_HoldRegData[24] = U32_GetHighWord(lw_PulseCntNum[11]);   // 脉冲通道11计数的高16位
    g_HoldRegData[25] = U32_GetLowWord( lw_PulseCntNum[11]);   // 脉冲通道11计数的低16位

    g_HoldRegData[26] = U32_GetHighWord(lw_PulseCntNum[12]);   // 脉冲通道12计数的高16位
    g_HoldRegData[27] = U32_GetLowWord( lw_PulseCntNum[12]);   // 脉冲通道12计数的低16位
    g_HoldRegData[28] = U32_GetHighWord(lw_PulseCntNum[13]);   // 脉冲通道13计数的高16位
    g_HoldRegData[29] = U32_GetLowWord( lw_PulseCntNum[13]);   // 脉冲通道13计数的低16位
    g_HoldRegData[30] = U32_GetHighWord(lw_PulseCntNum[14]);   // 脉冲通道14计数的高16位
    g_HoldRegData[31] = U32_GetLowWord( lw_PulseCntNum[14]);   // 脉冲通道14计数的低16位
    g_HoldRegData[32] = U32_GetHighWord(lw_PulseCntNum[15]);   // 脉冲通道15计数的高16位
    g_HoldRegData[33] = U32_GetLowWord( lw_PulseCntNum[15]);   // 脉冲通道15计数的低16位

}

